arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto);
[
    'push',
    'shift',
    'unshift',
    'pop',
    'splice',
    'reverse',
    'sort',
].forEach(function(method) {
    const original = arrayProto[method]
    Object.defineProperty(arrayMethods, method, {
        value: function mutator (...args) {
            return original.apply(this, args)
        }
    })
})

function defineReactive(obj, key, val) {
    observe(val);

    let dep = new Dep()

    Object.defineProperty(obj, key, {
        get() {
            Dep.target && dep.addDep(Dep.target)

            console.log('get ' + val);
            return val;
        },
        set(v) {
            if(v !== val) {
                val = v;
                console.log('set ' + v);
                if(typeof v === 'object'){
                    observe(v);
                }

                dep.update()
            }
        }
    })
}

function observe(obj){
    if(typeof obj !== 'object' || obj == null) {
        return
    }
    new Observer(obj);
}

function proxy(vm, sourceKey) {
    Object.keys(vm[sourceKey]).forEach(key => {     //将$data中的每个值直接代理到vue组件上
        Object.defineProperty(vm, key, {
            get() {
                return vm[sourceKey][key];
            },
            set(v) {
                vm[sourceKey][key] = v;
            }
        })
    })
}

class KVue {
    constructor(options) {
        this.$options = options;
        this.$data = options.data;

        //proxy
        proxy(this, '$data');

        //响应化处理
        observe(this.$data);

        //创建编译器
        new Compile(options.el, this)
    }
}

class Observer {
    constructor(value) {
        this.value = value;

        if(typeof value === 'object'){
            this.walk(value);
        }
    }

    walk(obj) {
        Object.keys(obj).forEach(key => {
            defineReactive(obj, key, obj[key])
        })
    }
}

// const watchers = []
//Watcher 在编译模板时创建 传入vue实例、key键、用于更新那个值的函数， 将那个值放入 Dep收集的依赖中
class Watcher {
    constructor(vm, key, updateFn) {
        this.vm = vm

        this.key = key

        this.updateFn = updateFn

        Dep.target = this
        this.vm[this.key]
        Dep.target = null
        // watchers.push(this)
    }

    update() {
        this.updateFn.call(this.vm, this.vm[this.key])
    }
}

//收集依赖
class Dep {
    constructor() {
        this.deps = [];
    }

    addDep (dep) {
        this.deps.push(dep);
    }

    update() {
        this.deps.forEach(w => {
            w.update()
        })
    }
}
